package stats;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;

import javax.swing.JFileChooser;
import javax.swing.JFrame;

import operators.AbstractArithmeticOperator;
import util.reflection.Reflection;
import util.resources.ResourceManager;

/**
 * A class which is used for numerical statistics
 * However, you need to manually set numerical variables each time before evaluation
 * @author Yin
 *
 */
public abstract class AbstractNumberStats implements IStats//extends AbstractStats 
{
	private Map<String, Double> myResults;
	private Map<String, Double> myVariables;
	private Map<String, AbstractArithmeticOperator> myOperators;
	private Map<String, Double> myInitialValues;
	
	private final String resourceFile = "operators";
	private ResourceManager myResourceManager = ResourceManager.getManager(resourceFile);
	
	protected static JFileChooser myChooser = new JFileChooser(System
            .getProperties().getProperty("user.dir"));
	
	public final static String PLUS = "+";
	public final static String MINUS = "-";
	public final static String TIMES = "*";
	public final static String DIVIDE = "/";
	public final static String MAX = "max";
	public final static String MIN = "min";
	public final static String REPLACE = "replace";
	
	public AbstractNumberStats()
	{
		myResults = new TreeMap<String, Double>();
		myOperators = new TreeMap<String, AbstractArithmeticOperator>();
		myVariables = new TreeMap<String, Double>();
		myInitialValues = new TreeMap<String, Double>();
	}
	
	@Override
	public void addStat(String eventName)
	{
		addStat(0.0, PLUS, 1.0, eventName);
	}
	
	public void addStat(double initial, String eventName)
	{
		addStat(initial, PLUS, 1.0, eventName);
	}
	
	public void addStat(double initial, String opeartion, String eventName)
	{
		addStat(initial, opeartion, 1.0, eventName);
	}
	
	/**
	 * Register an event to statistical result
	 * @param initial
	 * @param operation
	 * @param variable
	 * @param eventName
	 */
	public void addStat(double initial, String operation, double variable, String eventName)
	{
		setInitialValue(initial, eventName);
		setOperation(operation, eventName);
		setVariable(variable, eventName);
		myResults.put(eventName, initial);
	}
	
	/**
	 * Set initial Value for statistical result
	 * @param initial
	 * @param eventName
	 */
	public void setInitialValue(double initial, String eventName)
	{
		myInitialValues.put(eventName, initial);
	}
	
	/**
	 * Specify arithmetic operation
	 * @param operation
	 * @param eventName
	 */
	public void setOperation(String operation, String eventName)
	{
		AbstractArithmeticOperator operator = getOperator(operation);
		myOperators.put(eventName, operator);
	}
	
	/**
	 * Create proper operator from given token
	 * @param operation
	 * @return
	 */
	private AbstractArithmeticOperator getOperator(String operation)
	{
		String className = myResourceManager.getString(operation);
		AbstractArithmeticOperator
		operator = (AbstractArithmeticOperator) Reflection.createInstance(
				className, operation);
		return operator;
	}
	
	/**
	 * Set variable that is evaluated by operators
	 * @param variable
	 * @param eventName
	 */
	public void setVariable(double variable, String eventName)
	{
		myVariables.put(eventName, variable);
	}
	
	/**
	 * 
	 * @return all statistical results
	 */
	public Map<String, Double> getResults()
	{
		return myResults;
	}
	
	/**
	 * Return statistical result for given event name
	 * @param eventName
	 * @return
	 */
	public double getResult(String eventName)
	{
		return myResults.get(eventName);
	}
	
	/**
	 * Return true if this event is registered in statistics.
	 * @param eventName
	 * @return
	 */
	public boolean isEventExist(String eventName)
	{
		return myResults.containsKey(eventName);
	}
	
	/**
	 * Save all statistical results
	 */
	@Override
	public void save()
	{
		String s = new String();
		for (String str: myResults.keySet())
		{
			s += "/n" + str;
			s += " " + myInitialValues.get(str).toString();
			s += " " + myOperators.get(str).toString();
			s += " " + myVariables.get(str).toString();
		}
		
		int result = myChooser.showSaveDialog(new JFrame("Save File"));
		
		if (result == JFileChooser.APPROVE_OPTION) {
            File file = myChooser.getSelectedFile();
            try {
                FileWriter fw = new FileWriter(file);
                fw.write(s);
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
		
	}
	
	/**
	 * Load statistical results from text file.
	 */
	@Override
	public void load(String location)
	{
		myInitialValues.clear(); myOperators.clear(); myVariables.clear();
		
		File file = new File(location);
		Scanner scanner = new Scanner("");
		try
		{
			scanner = new Scanner(file);
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
		
		while(scanner.hasNextLine())
		{
			String[] line = scanner.nextLine().split("\\s");
			myInitialValues.put(line[0], Double.parseDouble(line[1]));
			myOperators.put(line[0], getOperator(line[2]));
			myVariables.put(line[0], Double.parseDouble(line[3]));
		}
	}
	
	/**
	 * Update statistics for given event
	 */
	@Override
	public double update(String eventName)
	{
		AbstractArithmeticOperator operator = myOperators.get(eventName);
		double[] variables = new double[2];
		variables[0] = myResults.get(eventName);
		variables[1] = myVariables.get(eventName);
		myResults.put(eventName, operator.evaluateSafe(variables));
		return operator.evaluateSafe(variables);
	}
	
	/**
	 * Reset statistics for given event to initial value
	 */
	@Override
	public void reset(String eventName)
	{
		myResults.put(eventName, myInitialValues.get(eventName));
	}
	
	/**
	 * Reset all statistics to initial values
	 */
	@Override
	public void resetAll()
	{
		for (String str: myResults.keySet())
			reset(str);
	}
	
}
